#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/socket.h>
#include<netinet/in.h> //struct sockaddr_in
#include<ctype.h> //toupper()
#include<arpa/inet.h> //inet_ntop
#include <signal.h>
#include<string.h>//bzero()
#include<sys/wait.h> //wait()

#include"socket_wrap.h"

#define SRV_PORT 10008

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

void catch_child(int signo){
    pid_t wpid;

    while((wpid=wait(NULL))!=-1){//利用循环解决同同一时间接收到多个子进程的死亡信号导致有的子进程未被回收的问题
        printf("利用信号回收子进程%d\n",signo);
    }
    return;
}


int main(int argc, char *argv[])
{
	int lfd;//门卫套接字的文件描述符
	int cfd;//与客户端建立好通信后的文件描述符
	struct sockaddr_in serv_addr;
	struct sockaddr_in clnt_addr;
	socklen_t clnt_addr_size;
	int opt=1;

	pid_t pid;
	int ret,i;
	char buf[BUFSIZ];//大小8192，我去这个是系统给的一个buf大小
	
	//s
	lfd=Socket(PF_INET, SOCK_STREAM, 0);

	//设置端口复用（必须放在绑定地址结构之前）:服务器主动断开连接后不用再等待2WSL
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(opt));//问题：开启了端口复用第一个客户端响应正常，但是第二个客户端连接后发信息没有回信，为什么出问题了？？？
	setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,(void *)&opt,sizeof(opt));

	//b:设置地址族：地址族为ipv4、ip号、端口号
	//memset(&serv_addr, 0, sizeof(serv_addr));
	bzero(&serv_addr,sizeof(serv_addr));//置零，我的评价是比memset()好用
	serv_addr.sin_family=PF_INET;//ipv4
	serv_addr.sin_port=htons(SRV_PORT);//port:如果port用宏则不用atoi()
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//ip
	Bind(lfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr));
 
	//l:它不是等待监听，是设置监听上限（同时建立三次握手的客户端数量）
	Listen(lfd, 128);
		
	//a:父进程不断监听，子进程负责和客户端通信
	clnt_addr_size=sizeof(clnt_addr);  
	while (1){
		cfd=Accept(lfd, (struct sockaddr*)&clnt_addr,&clnt_addr_size);//第二个参数是传出参数：存储客户端的ip和port//第三个是传入传出参数：传入addr的大小，传出addr的实际大小
		
		pid=fork();
		if(pid<0){
			perr_exit("fork出错");
		}else if(0==pid){
			close(lfd);//子进程无需监听，交给父进程去做
			break;
		}else{//我感觉这里这不是重复注册了？？？
			//注册信号捕捉(似乎就是固定的5步.)
			struct sigaction act;
			act.sa_handler=catch_child;
			sigemptyset(&act.sa_mask);
			act.sa_flags=0;
			ret=sigaction(SIGCHLD,&act,NULL);
			if(ret!=0){
				perr_exit("注册信号失败");
			}

			close(cfd);//父进程无需与客户端通信
		}
	
	}
	
	//子进程不放到while(1)中避免拥挤
	if(0==pid){
		while(1){//服务端不停回复 原来如此简单，加个while(1)就可以了
			ret=Read(cfd,buf,sizeof(buf));
			if(ret==0){
				close(cfd);//检测到客户端close()传递EOF,read()后返回0
				exit(1);//本进程退出
			}

			for(i=0;i<ret;++i){//转为大写传回客户端
				buf[i]=toupper(buf[i]);
			}
			write(cfd,buf,ret);
			write(STDOUT_FILENO,buf,ret);//写到服务器屏幕方便观察
		}
	}

	//close(cfd);//通信socket	
	//close(lfd);//门卫socket
	return 0;
}





